﻿using LibreriaAcceso;
using LibreriaAcceso.Clases;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using System;
using System.Data.Common;
using System.Threading.Tasks;
using System.Transactions;
using System.Web;
namespace ASPNetPatterns.Controllers
{

    public class IdentityAsp
    {
        #region campos
        /// <summary>
        /// field tipo UserStore, permite el acceso a la manera persistente en donde se almacenan los usuarios
        /// </summary>
        UserStore<IdentityUser> userStore;

        /// <summary>
        /// Manager/Administración de usuarios, utiliza el userStore para ser inicializado
        /// </summary>
        UserManager<IdentityUser> manager;
        #endregion

        #region propiedades
        /// <summary>
        /// Propiedad que devuelve el campo userStore
        /// </summary>
        UserStore<IdentityUser> UserStore
        {
            get
            {
                if (userStore == null)
                    userStore = new UserStore<IdentityUser>();
                return userStore;
            }
        }

        /// <summary>
        /// Propiedad que devuelve el manager de usuarios
        /// </summary>
        UserManager<IdentityUser> Manager
        {
            get
            {
                if (manager == null)
                    InitializeManager();
                return manager;
            }
        }

        #endregion

        #region métodos públicos
        /// <summary>
        /// Método para registrar usuario con ASP Identity.
        /// </summary>
        /// <param name="username">usuario</param>
        /// <param name="password">contraseña</param>
        /// <returns></returns>
        public void RegistrarUsuario(string username, string password)
        {
            UserStore.Context.Database.Connection.Open();
            using (var ts = UserStore.Context.Database.Connection.BeginTransaction())
            {
                try
                {
                    IdentityResult result = CreateIdentityUser(username, password, ts);
                    if (result.Succeeded)
                    {
                        ts.Commit();
                    }
                    else
                    {
                        //Metodo Helper que lee los errores al crear un usuario y lanza la excepción con cada uno de estos errores en la propiedad Data de
                        //la excepción
                        throw Helpers.ExceptionsHelper.ReadIdentityResultErrorsAndReturnException(result.Errors);
                    }
                }
                catch (Exception)
                {
                    ts.Rollback();
                    throw;
                }
                finally 
                {
                    //Cerramos la conexion
                    UserStore.Context.Database.Connection.Close();
                }   
            }
        }

       
        /// <summary>
        /// Método para autenticar un usuario con ASP Identity
        /// </summary>
        /// <param name="username">usuario</param>
        /// <param name="password">contraseña</param>
        /// <returns></returns>
        public bool AutenticarUsuario(string username, string password, bool esPersistente)
        {
            //Buscamos el 
            var usuario = Manager.Find(username, password);

            if (usuario == null)
                throw new ASPNetPatterns.Models.IdentityAspException("Error al autenticar el usuario. Verifica usuario y contraseña.");

            CrearCookieIdentidadYSignIn(usuario, esPersistente);
            return true;
        }

        /// <summary>
        /// Método para cerrar sesión en el contexto web de la aplicación.
        /// </summary>
        public void CerrarSesion()
        {
            var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
            authenticationManager.SignOut();
        }

        #endregion

        #region métodos privados

        /// <summary>
        /// Inizializa el manager UserManager
        /// </summary>
        private void InitializeManager()
        {
            manager = new UserManager<IdentityUser>(UserStore);
            ///Aclaramos que el usuario puede ser un usuario Alfanumerico y con caracteres especiales
            manager.UserValidator = new UserValidator<IdentityUser>(manager) { AllowOnlyAlphanumericUserNames = false };
        }

        /// <summary>
        /// Método privado que pone en contexto al usuario que se firmará y lo firma.
        /// </summary>
        /// <param name="usuario"></param>
        /// <param name="esPersistente"></param>
        private void CrearCookieIdentidadYSignIn(IdentityUser usuario, bool esPersistente)
        {
            //Obtenemos el contexto web la propiedad de autenticación
            var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
            authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
            var userIdentity = Manager.CreateIdentity(usuario, DefaultAuthenticationTypes.ApplicationCookie);
            authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = esPersistente }, userIdentity);
        }
        /// <summary>
        /// Crea usuario tipo Identity
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="ts"></param>
        /// <returns></returns>
        private IdentityResult CreateIdentityUser(string username, string password, DbTransaction ts)
        {
            var user = new IdentityUser() { UserName = username };
            UserStore.Context.Database.UseTransaction(ts);
            IdentityResult result = Manager.Create(user, password);
            return result;
        }
        

        #endregion
    }
}